#include <ptgui/ptgui.h>
#include <ptgui/dc.h>
#include <ptgui/driver.h>
#include <ptgui/system.h>
#include <ptgui/region.h>

static void ptgui_dc_hw_draw_point(struct ptgui_dc *dc, rt_int16_t x, rt_int16_t y);
static void ptgui_dc_hw_draw_hline(struct ptgui_dc *dc, rt_int16_t x1, rt_int16_t x2, rt_int16_t y);
static void ptgui_dc_hw_draw_vline(struct ptgui_dc *dc, rt_int16_t x, rt_int16_t y1, rt_int16_t y2);
static void ptgui_dc_hw_fill_rect(struct ptgui_dc *dc, ptgui_rect_t *rect);
static void ptgui_dc_hw_blit(struct ptgui_dc *dc, struct ptgui_point *dc_point, struct ptgui_dc *dest,
							 ptgui_rect_t *rect);
static void ptgui_dc_hw_set_color(struct ptgui_dc *dc, ptgui_color_t color);
static ptgui_color_t ptgui_dc_hw_get_color(struct ptgui_dc *dc);
static void ptgui_dc_hw_set_bg_color(struct ptgui_dc *dc, ptgui_color_t color);
static ptgui_color_t ptgui_dc_hw_get_bg_color(struct ptgui_dc *dc);
static void ptgui_dc_hw_set_font(struct ptgui_dc *dc, ptgui_font_t *font);
static ptgui_font_t *ptgui_dc_hw_get_font(struct ptgui_dc *dc);
static void ptgui_dc_hw_set_textalign(struct ptgui_dc *dc, rt_int32_t textalign);
static rt_int32_t ptgui_dc_hw_get_textalign(struct ptgui_dc *dc);
static rt_bool_t ptgui_dc_hw_fini(struct ptgui_dc *dc);
static rt_bool_t ptgui_dc_hw_get_visible(struct ptgui_dc *dc);
static void ptgui_dc_hw_get_rect(struct ptgui_dc *dc, ptgui_rect_t *rect);

struct ptgui_dc * ptgui_dc_begin_drawing(ptgui_widget_t *owner)
{
	return ptgui_dc_hw_create(owner);
}

void ptgui_dc_end_drawing(struct ptgui_dc *dc)
{
	if (ptgui_dc_hw_fini(dc) == RT_TRUE) {
		ptgui_free(dc);
	}
}

void ptgui_dc_hw_init(struct ptgui_dc_hw *dc)
{
	if (dc == RT_NULL) {
		return;
	}

	dc->parent.type = PTGUI_DC_HW;
	dc->parent.draw_point = ptgui_dc_hw_draw_point;
	dc->parent.draw_hline = ptgui_dc_hw_draw_hline;
	dc->parent.draw_vline = ptgui_dc_hw_draw_vline;
	dc->parent.fill_rect = ptgui_dc_hw_fill_rect ;
	dc->parent.blit = ptgui_dc_hw_blit;

	dc->parent.set_color = ptgui_dc_hw_set_color;
	dc->parent.get_color = ptgui_dc_hw_get_color;

	dc->parent.set_bg_color = ptgui_dc_hw_set_bg_color;
	dc->parent.get_bg_color = ptgui_dc_hw_get_bg_color;

	dc->parent.set_font = ptgui_dc_hw_set_font;
	dc->parent.get_font = ptgui_dc_hw_get_font;
	dc->parent.set_textalign = ptgui_dc_hw_set_textalign;
	dc->parent.get_textalign = ptgui_dc_hw_get_textalign;

	dc->parent.get_visible = ptgui_dc_hw_get_visible;
	dc->parent.get_rect = ptgui_dc_hw_get_rect;

	dc->parent.fini = ptgui_dc_hw_fini;
}

extern struct rt_mutex cursor_mutex;
#define dc_set_foreground(c) gui_content..foreground = c
#define dc_set_background(c) gui_content..background = c
extern void ptgui_mouse_show_cursor(void);
extern void ptgui_mouse_hide_cursor(void);
struct ptgui_dc * ptgui_dc_hw_create(ptgui_widget_t *owner)
{
	struct ptgui_dc_hw *dc;

	/* adjudge owner */
	if (owner == RT_NULL) {
		return RT_NULL;
	}

	/* malloc a dc object */
	dc = (struct ptgui_dc_hw *)ptgui_malloc(sizeof(struct ptgui_dc_hw));
	ptgui_dc_hw_init(dc);
	dc->owner = owner;
	dc->visible = RT_TRUE;
	dc->device = ptgui_graphic_driver_get_default();	

	return &(dc->parent);
}

static rt_bool_t ptgui_dc_hw_fini(struct ptgui_dc *dc)
{
	struct ptgui_dc_hw *hw = (struct ptgui_dc_hw *)dc;
	if (dc == RT_NULL || hw->parent.type != PTGUI_DC_HW) {
		return RT_FALSE;
	}

#ifdef __WIN32__
#ifdef PTGUI_USING_MOUSE_CURSOR
	rt_mutex_release(&cursor_mutex);
	/* show cursor */
	ptgui_mouse_show_cursor();
	rt_kprintf("show cursor\n");
#endif
	/* update screen */
	hw->device->screen_update(&(hw->owner->extent));
#else
#ifdef PTGUI_USING_MOUSE_CURSOR
	/* show cursor */
	ptgui_mouse_show_cursor();
#endif

	/* update screen */
	hw->device->screen_update(&(hw->owner->extent));
#endif

	return RT_TRUE;
}

/*
 * draw a logic point on device
 */
static void ptgui_dc_hw_draw_point(struct ptgui_dc *self, rt_int16_t x, rt_int16_t y)
{
	struct ptgui_dc_hw *dc;

	dc = (struct ptgui_dc_hw *)self;
	if (dc == RT_NULL || dc->visible != RT_TRUE) {
		return;
	}

	x = x + dc->owner->extent.x1;
	y = y + dc->owner->extent.y1;

	if (ptgui_rect_contains_point(&(dc->owner->extent), x, y) == RT_EOK) {
		/* draw this point */
		dc->device->set_pixel(&(gui_content.foreground), x, y);
	}
}

/*
 * draw a logic vertical line on device
 */
static void ptgui_dc_hw_draw_vline(struct ptgui_dc *self, rt_int16_t x, rt_int16_t y1, rt_int16_t y2)
{
	struct ptgui_dc_hw *dc;
	ptgui_rect_t *prect;

	dc = (struct ptgui_dc_hw *)self;
	if (dc == RT_NULL || dc->visible != RT_TRUE) {
		return;
	}

	x = x + dc->owner->extent.x1;
	y1 = y1 + dc->owner->extent.y1;
	y2 = y2 + dc->owner->extent.y1;

	prect = &(dc->owner->extent);

	/* calculate vline intersect */
	if (prect->x1 > x || prect->x2 <= x) {
		return;
	}
	if (prect->y2 <= y1 || prect->y1 > y2) {
		return;
	}

	if (prect->y1 > y1) {
		y1 = prect->y1;
	}
	if (prect->y2 < y2) {
		y2 = prect->y2;
	}

	/* draw vline */
	dc->device->draw_vline(&(gui_content.foreground), x, y1, y2);
}

/*
 * draw a logic horizontal line on device
 */
static void ptgui_dc_hw_draw_hline(struct ptgui_dc *self, rt_int16_t x1, rt_int16_t x2, rt_int16_t y)
{
	struct ptgui_dc_hw *dc;
	ptgui_rect_t *prect;

	dc = (struct ptgui_dc_hw *)self;
	if (dc == RT_NULL || dc->visible != RT_TRUE) {
		return;
	}

	/* convert logic to device */
	x1 = x1 + dc->owner->extent.x1;
	x2 = x2 + dc->owner->extent.x1;
	y = y + dc->owner->extent.y1;

	prect = &(dc->owner->extent);

	/* calculate vline intersect */
	if (prect->y1 > y || prect->y2 <= y) {
		return;
	}
	if (prect->x2 <= x1 || prect->x1 > x2) {
		return;
	}

	if (prect->x1 > x1) {
		x1 = prect->x1;
	}
	if (prect->x2 < x2) {
		x2 = prect->x2;
	}

	/* draw hline */
	dc->device->draw_hline(&(gui_content.foreground), x1, x2, y);
}

static void ptgui_dc_hw_fill_rect(struct ptgui_dc *self, struct ptgui_rect *rect)
{
	ptgui_color_t foreground;
	register rt_int16_t index;
	struct ptgui_dc_hw *dc;

	dc = (struct ptgui_dc_hw *)self;
	if (dc == RT_NULL || dc->visible != RT_TRUE) {
		return;
	}

	/* save foreground color */
	foreground = gui_content.foreground;

	/* set background color as foreground color */
	gui_content.foreground = gui_content.background;

	/* fill rect */
	for (index = rect->y1; index < rect->y2; index ++) {
		ptgui_dc_hw_draw_hline(self, rect->x1, rect->x2, index);
	}

	/* restore foreground color */
	gui_content.foreground = foreground;
}

static void ptgui_dc_hw_blit(struct ptgui_dc *dc, struct ptgui_point *dc_point, struct ptgui_dc *dest,
							 ptgui_rect_t *rect)
{
	/* not blit in hardware dc */
	return ;
}

static void ptgui_dc_hw_set_color(struct ptgui_dc *self, ptgui_color_t color)
{
	struct ptgui_dc_hw *dc = (struct ptgui_dc_hw *)self;

	if (self != RT_NULL) {
		gui_content.foreground = color;
	}
}

static ptgui_color_t ptgui_dc_hw_get_color(struct ptgui_dc *self)
{
	struct ptgui_dc_hw *dc = (struct ptgui_dc_hw *)self;

	return self != RT_NULL ? gui_content.foreground : COLOR_BLACK;
}

static void ptgui_dc_hw_set_bg_color(struct ptgui_dc *self, ptgui_color_t color)
{
	struct ptgui_dc_hw *dc = (struct ptgui_dc_hw *)self;

	if (self != RT_NULL) {
		gui_content.background = color;
	}
}

static ptgui_color_t ptgui_dc_hw_get_bg_color(struct ptgui_dc *self)
{
	struct ptgui_dc_hw *dc = (struct ptgui_dc_hw *)self;

	return self != RT_NULL ? gui_content.background : COLOR_WHITE;
}

static void ptgui_dc_hw_set_font(struct ptgui_dc *self, ptgui_font_t *font)
{
	struct ptgui_dc_hw *dc = (struct ptgui_dc_hw *)self;

	if (self != RT_NULL) {
		gui_content.font = font;
	}
}

static ptgui_font_t * ptgui_dc_hw_get_font(struct ptgui_dc *self)
{
	struct ptgui_dc_hw *dc = (struct ptgui_dc_hw *)self;

	return self != RT_NULL ? gui_content.font : RT_NULL;
}

static void ptgui_dc_hw_set_textalign(struct ptgui_dc *self, rt_int32_t textalign)
{
	struct ptgui_dc_hw *dc = (struct ptgui_dc_hw *)self;

	gui_content.text_align = textalign;
}

static rt_int32_t ptgui_dc_hw_get_textalign(struct ptgui_dc *self)
{
	struct ptgui_dc_hw *dc = (struct ptgui_dc_hw *)self;

	return gui_content.text_align;
}

static rt_bool_t ptgui_dc_hw_get_visible(struct ptgui_dc *self)
{
	struct ptgui_dc_hw *dc = (struct ptgui_dc_hw *)self;

	return dc->visible;
}

static void ptgui_dc_hw_get_rect(struct ptgui_dc *self, ptgui_rect_t *rect)
{
	struct ptgui_dc_hw *dc = (struct ptgui_dc_hw *)self;

	//ptgui_widget_get_rect(dc->owner, rect);
}
